package com.milo.stream;

import com.alibaba.fastjson.JSONObject;
import com.milo.stream.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 测试Collectors工具类
 * @author milogenius
 * @date 2020-10-21 16:12
 */
@Slf4j
public class CollectorsTest {

    /**
     *toCollection
     */
    @Test
    public void toCollection() {
        //List
        ArrayList<Integer> collect = Stream.of(1, 2, 3, 4, 5, 6, 8, 9, 0).collect(Collectors.toCollection(ArrayList::new));
        //Set
        HashSet<Integer> hashSet = Stream.of(1,2,3,4,5,6,8,9,0).collect(Collectors.toCollection(HashSet::new));
    }

    /**
     *toList
     */
    @Test
    public void toList() {
        //List
        List<Integer> collect = Stream.of(1, 2, 3, 4, 5, 6, 8, 9, 0).collect(Collectors.toList());
    }

    /**
     *toSet
     */
    @Test
    public void toSet() {
        //Set
        Set<Integer> collect = Stream.of(1, 2, 3, 4, 5, 6, 8, 9, 0).collect(Collectors.toSet());
    }

    /**
     *toMap
     */
    @Test
    public void toMap() {
        Student studentA = new Student("20190001","小明");
        Student studentB = new Student("20190002","小红");
        Student studentC = new Student("20190003","小丁");
        Student studentD = new Student("20190003","小李");

        //Function.identity() 获取这个对象本身，那么结果就是Map<String,Student> 即 id->student
        //串行收集
        Map<String, Student> collect = Stream.of(studentA, studentB, studentC)
                .collect(Collectors.toMap(Student::getId, Function.identity()));
        log.info("对象自身result:\n{}",JSONObject.toJSONString(collect));

        //Map<String,String> 即 id->name
        //串行收集
        Map<String, String> collect1 = Stream.of(studentA, studentB, studentC)
                .collect(Collectors.toMap(Student::getId, Student::getName));
        log.info("name_result:\n{}",JSONObject.toJSONString(collect1));

        //Map<String,Student>
        //假设有两个id相同Student，如果他们id相同，在转成Map的时候，取name大一个，小的将会被丢弃
        Map<String, Student> collect2 = Stream.of(studentA, studentB, studentC,studentD)
                .collect(Collectors
                        .toMap(Student::getId, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(Student::getName))));
        log.info("id冲突:{}",JSONObject.toJSONString(collect2));

        //自定义LinkedHashMap
        //Map<String,Student>
        LinkedHashMap<String,Student> list = Stream.of(studentA, studentB, studentC)
                .collect(Collectors
                        .toMap(Student::getId,
                                Function.identity(),
                                BinaryOperator
                                        .maxBy(Comparator.comparing(Student::getName)),
                                LinkedHashMap::new));
    }

    /**
     *演示toMap方法中key冲突如何解决问题
     */
    @Test
    public void toMapForKeyConflict() {
        List<HashMap<String, String>> input = new ArrayList<>();

        HashMap<String, String> subinput1 = new HashMap<>();
        subinput1.put("name", "name1");
        subinput1.put("rollno", "rollno1");
        input.add(subinput1);

        HashMap<String, String> subinput2 = new HashMap<>();
        subinput2.put("name", "name2");
        subinput2.put("rollno", "rollno2");
        input.add(subinput2);

        //Test key conflict
        HashMap<String, String> subinput3 = new HashMap<>();
        subinput3.put("name", "name3");
        subinput3.put("rollno", "rollno2");
        input.add(subinput3);

        System.out.println("输入:"+ JSONObject.toJSONString(input));
        HashMap<String, HashMap<String, String>> result = input.stream()
                .collect(Collectors.toMap(v -> (String) v.get("rollno"), Function.identity(), (oldValue, newValue) -> newValue, HashMap::new));
        System.out.println("输出"+JSONObject.toJSONString(result));
    }

    /**
     *toConcurrentMap
     */
    @Test
    public void toConcurrentMap() {
        Student studentA = new Student("20190001","小明");
        Student studentB = new Student("20190002","小红");
        Student studentC = new Student("20190003","小丁");
        //并发收集
        ConcurrentMap<String, Student> collect = Stream.of(studentA, studentB, studentC)
                .parallel()
                .collect(Collectors.toConcurrentMap(Student::getId, Function.identity()));
        log.info("result:\n{}",JSONObject.toJSONString(collect));

        //并发收集
        ConcurrentMap<String, String> collect1 = Stream.of(studentA, studentB, studentC)
                .parallel()
                .collect(Collectors.toConcurrentMap(Student::getId, Student::getName));

        log.info("result:\n{}",JSONObject.toJSONString(collect1));

    }

    /**
     *joining:拼接
     */
    @Test
    public void joining() {
        Student studentA = new Student("20190001", "小明");
        Student studentB = new Student("20190002", "小红");
        Student studentC = new Student("20190003", "小丁");

        //使用分隔符：201900012019000220190003
        String collect = Stream.of(studentA, studentB, studentC)
                .map(Student::getId)
                .collect(Collectors.joining());
        log.info(JSONObject.toJSONString(collect));

        //使用^_^ 作为分隔符
        //20190001^_^20190002^_^20190003
        String collect1 = Stream.of(studentA, studentB, studentC)
                .map(Student::getId)
                .collect(Collectors.joining("^_^"));
        log.info(JSONObject.toJSONString(collect1));

        //使用^_^ 作为分隔符
        //[]作为前后缀
        //[20190001^_^20190002^_^20190003]
        String collect2 = Stream.of(studentA, studentB, studentC)
                .map(Student::getId)
                .collect(Collectors.joining("^_^", "[", "]"));
        log.info(JSONObject.toJSONString(collect2));
    }

    /**
     *counting
     */
    @Test
    public void counting() {
        // Long 8
        Long collect = Stream.of(1, 0, -10, 9, 8, 100, 200, -80)
                .collect(Collectors.counting());
        log.info(JSONObject.toJSONString(collect));
        //如果仅仅只是为了统计，那就没必要使用Collectors了，那样更消耗资源
        // long 8
        long count = Stream.of(1, 0, -10, 9, 8, 100, 200, -80)
                .count();

        log.info(JSONObject.toJSONString(count));
    }

    /**
     *maxBy:求最大值
     */
    @Test
    public void maxBy() {
        // maxBy 200
        Stream.of(1, 0, -10, 9, 8, 100, 200, -80)
                .collect(Collectors.maxBy(Integer::compareTo)).ifPresent(System.out::println);

        // max 200
        Stream.of(1, 0, -10, 9, 8, 100, 200, -80)
                .max(Integer::compareTo).ifPresent(System.out::println);
    }

    /**
     *minBy:求最小值
     */
    @Test
    public void minBy() {
        // minBy -80
        Stream.of(1, 0, -10, 9, 8, 100, 200, -80)
                .collect(Collectors.minBy(Integer::compareTo)).ifPresent(System.out::println);

        // min -80
        Stream.of(1, 0, -10, 9, 8, 100, 200, -80)
                .min(Integer::compareTo).ifPresent(System.out::println);
    }

    /**
     *summingInt:返回的是一个IntSummaryStatistics(求总)，包含了数量统计count、求和sum、最小值min、平均值average、最大值max
     */
    @Test
    public void summingInt( ) {
        //IntSummaryStatistics{count=10, sum=55, min=1, average=5.500000, max=10}
        IntSummaryStatistics collect = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                .collect(Collectors.summarizingInt(Integer::valueOf));
        log.info(JSONObject.toJSONString(collect));
    }

    /**
     *summarizingDouble:返回的是一个DoubleSummaryStatistics(求总)，包含了数量统计count、求和sum、最小值min、平均值average、最大值max
     */
    @Test
    public void summarizingDouble() {
        //DoubleSummaryStatistics{"average":5.5,"count":10,"max":10.0,"min":1.0,"sum":55.0}
        DoubleSummaryStatistics collect = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                .collect(Collectors.summarizingDouble(Double::valueOf));
        log.info(JSONObject.toJSONString(collect));
    }

    /**
     *summarizingLong:返回的是一个LongSummaryStatistics(求总)，包含了数量统计count、求和sum、最小值min、平均值average、最大值max
     */
    @Test
    public void summarizingLong() {
        LongSummaryStatistics collect = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                .collect(Collectors.summarizingLong(Long::valueOf));
        log.info(JSONObject.toJSONString(collect));
    }

    /**
     *averagingInt:求平均值
     */
    @Test
    public void averagingInt() {
        Double collect = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                .collect(Collectors.averagingInt(Integer::valueOf));
        log.info(JSONObject.toJSONString(collect));
    }

    /**
     *averagingDouble
     */
    @Test
    public void averagingDouble() {
        Double collect = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                .collect(Collectors.averagingDouble(Double::valueOf));
        log.info(JSONObject.toJSONString(collect));
    }

    /**
     *averagingLong
     */
    @Test
    public void averagingLong() {
        Double doubles = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                .collect(Collectors.averagingLong(Long::valueOf));
        log.info(JSONObject.toJSONString(doubles));
    }

    /**
     *groupingBy:根据条件分组
     */
    @Test
    public void groupingBy() {
        Map<String, List<Integer>> collect = Stream.of(-6, -7, -8, -9, 0, 1, 2, 3, 4, 5, 6)
                .collect(Collectors.groupingBy(integer -> {
                    if (integer < 0) {
                        return "小于";
                    } else if (integer == 0) {
                        return "等于";
                    } else {
                        return "大于";
                    }
                }));
        log.info(JSONObject.toJSONString(collect));

        //Map<String,Set<Integer>>
        //自定义下游收集器
        Map<String, Set<Integer>> collect1 = Stream.of(-6, -7, -8, -9, 1, 2, 3, 4, 5, 6)
                .collect(Collectors.groupingBy(integer -> {
                    if (integer < 0) {
                        return "小于";
                    } else if (integer == 0) {
                        return "等于";
                    } else {
                        return "大于";
                    }
                }, Collectors.toSet()));
        log.info(JSONObject.toJSONString(collect1));

        //Map<String,Set<Integer>>
        //自定义map容器 和 下游收集器
        LinkedHashMap<String, Set<Integer>> collect2 = Stream.of(-6, -7, -8, -9, 1, 2, 3, 4, 5, 6)
                .collect(Collectors.groupingBy(integer -> {
                    if (integer < 0) {
                        return "小于";
                    } else if (integer == 0) {
                        return "等于";
                    } else {
                        return "大于";
                    }
                }, LinkedHashMap::new, Collectors.toSet()));
        log.info(JSONObject.toJSONString(collect2));

    }

    /**
     *partitioningBy:分区,因为方法参数Predicate只能返回true或false;所以只能分为两类
     */
    @Test
    public void partitioningBy() {
        //Map<Boolean,List<Integer>>
        Map<Boolean, List<Integer>> collect = Stream.of(0, 1, 0, 1)
                .collect(Collectors.partitioningBy(integer -> integer == 0));
        log.info(JSONObject.toJSONString(collect));

        //Map<Boolean,Set<Integer>>
        //自定义下游收集器
        Map<Boolean, Set<Integer>> collect1 = Stream.of(0, 1, 0, 1)
                .collect(Collectors.partitioningBy(integer -> integer == 0, Collectors.toSet()));
        log.info(JSONObject.toJSONString(collect1));
    }

    /**
     *mapping:自定义要收集的内容
     */
    @Test
    public void mapping() {
        Student studentA = new Student("20190001", "小明");
        Student studentB = new Student("20190002", "小红");
        Student studentC = new Student("20190003", "小丁");
        //List<String>
        List<String> collect = Stream.of(studentA, studentB, studentC)
                .collect(Collectors.mapping(Student::getName, Collectors.toList()));
        log.info(JSONObject.toJSONString(collect));
    }

    /**
     *collectingAndThen:如果你要在收集数据后再做一些操作，那么这个就非常有用了
     */
    @Test
    public void collectingAndThen() {
        Student studentA = new Student("20190001", "小明");
        Student studentB = new Student("20190002", "小红");
        Student studentC = new Student("20190003", "小丁");
        //listIterator 这里在收集后转成了listIterator
        ListIterator<Student> collect = Stream.of(studentA, studentB, studentC)
                .collect(Collectors.collectingAndThen(Collectors.toList(), List::listIterator));
        log.info(JSONObject.toJSONString(collect));
    }


}
